package com.javasearch.index;

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import com.javasearch.MainApplication;
import com.javasearch.index.build.ClassBuild;
import com.javasearch.Config;
import com.javasearch.utils.ExecutorUtils;
import com.javasearch.utils.FileUtil;
import com.javasearch.utils.JarUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.jar.JarEntry;

public class JarIndex   {
    private static final Logger logger = LoggerFactory.getLogger(JarIndex.class);

    public static void buildIndex(String getProjectPath) {
        String[] split = getProjectPath.split(",");
        List<String> jarInfo = new ArrayList<>();
        List<String> filesAll= new ArrayList<>();
        List<Future<?>> futures = new ArrayList<>();
        for (String path : split) {
            filesAll.addAll(FileUtil.getFilesAll(path, ".*.jar$"));
        }
        String[] array = filesAll.stream().filter(jar -> !jar.endsWith("javadoc.jar")&&!jar.endsWith("sources.jar")).toArray(String[]::new);
        // 创建布隆过滤器对象
        BloomFilter<String> bitSet = BloomFilter.create(
                Funnels.stringFunnel(Charset.defaultCharset()),
                array.length,
                0.01);

        Arrays.stream(array).forEach(jar -> {
            Future<?> jar1 = MainApplication.executor.submit(() -> {
                File file = new File(jar);
                synchronized (MainApplication.class) {
                    if (bitSet.mightContain(file.getName())) {
                        return;
                    }
                    bitSet.put(file.getName());
                }
                List<String> list = addJarTOJvm(jar);
                synchronized (MainApplication.class) {
                    jarInfo.addAll(list);
                }

            });
            futures.add(jar1);
        });
        //等待执行完毕
        ExecutorUtils.waitComplete(futures);
        futures.clear();
        filesAll.clear();
        initClass(jarInfo);
    }
    //将jar加入到jvm中然后返回路径
    public static   List<String> addJarTOJvm(String jarPath){
        List<String> jarInfo = new ArrayList<>();
        try {
            File file = new File(jarPath);
            MainApplication.jarClassLoader.addJar(file);
            //读取jar包中的所有类
            Map<JarEntry, InputStream> jarEntryInputStreamMap = JarUtil.readJarFile(jarPath, ".*.class$");
            Set<JarEntry> jarEntries = jarEntryInputStreamMap.keySet();
            for (JarEntry jarEntry : jarEntries) {
                String name = jarEntry.getName();
                //去掉.class和/
                String name1 = name.substring(0, name.length() - 6);
                name1 = name1.replace("/", ".");
                String fullPath = file.getAbsolutePath() + "/" + name + "&&" + name1;
                jarInfo.add(fullPath);
            }
        } catch (IOException e) {
            logger.error("addJarTOJvm-读取jar包失败:", e);
        }
        return jarInfo;
    }


    public  static    void initClass(List<String> JarInfo){
        List<Future<?>> futures = new ArrayList<>();
        Map<String,Class> map=new ConcurrentHashMap<>();
        int i1 = Math.max(JarInfo.size() / 10, 20);
        //装载类
        for (int i = 0; i < i1; i++) {
            //将JarInfo分为20份
            int finalI = i;
            Future<?> jar1 = MainApplication.executor.submit(() -> {
                for (int j = 0; j < JarInfo.size(); j++) {
                    if (j % i1 == finalI) {
                        String[] split = JarInfo.get(j).split("&&");
                        String fillPath = split[0];
                        String classPath = split[1];
                        try {
                            Class<?> aClass = MainApplication.jarClassLoader.loadClass(classPath);
                            map.put(fillPath, aClass);
                        } catch (NoClassDefFoundError | VerifyError | IncompatibleClassChangeError |
                                 UnsupportedClassVersionError | Exception e3) {
//                            logger.error("装载类失败:{},错误信息:", fillPath, e3);
                        }
                    }
                }
            });
            futures.add(jar1);
        }
        ExecutorUtils.waitComplete(futures);
        futures.clear();
        ClassBuild classBuild=new ClassBuild();
        classBuild.Class(map);
        map.clear();
    }

}
